/**
 * Unpacks a Promise<T> to T
 * @see {@link https://stackoverflow.com/questions/48011353/how-to-unwrap-type-of-a-promise#answer-57364353}
 */
export type Await<T> = T extends {
    then(onfulfilled?: (value: infer U) => unknown): unknown;
} ? U : T;

/**
 * Get type of a property
 * @see {@link https://stackoverflow.com/questions/45894524/getting-type-of-a-property-of-a-typescript-class-using-keyof-operator#answer-54432326}
 */
export type Property<TObj, TProp extends keyof TObj> = TObj[TProp];

/**
 * An asynchronous implementation of sleep, `await` on me!
 * @param ms - microseconds
 */
export async function sleep(ms: number) {
  return new Promise(r => setTimeout(r, ms));
}

export function _get_char_count(s: string): Map<string, number> {
  let wc = new Map<string, number>();
  for (let c of s) {
    wc.set(c, (wc.get(c) ?? 0) + 1);
  }
  return wc;
}

/**
 * Calculate consine-like distance between two strings
 * @param lhs - base
 * @param rhs - operand
 * @returns distance (fraction wrt `lhs`)
 */
export function distance(lhs: string, rhs: string): number {
  let [lhswc, rhswc] = [lhs, rhs].map(_get_char_count);
  let [total, diff] = [0, 0];
  for (let [c, lc] of lhswc) {
    total += lc;
    diff += Math.abs(lc - (rhswc.get(c) ?? 0));
    rhswc.delete(c);
  }
  for (let [_, rc] of rhswc)
    diff += rc;
  return diff / total;
}
